<?php

namespace app\yy\services;

use app\yy\model\Ip;
use app\yy\model\Domain;
use app\yy\model\IpRecord;
use app\yy\model\IpRequestRecord;
use app\yy\model\IpStatistics;

class IpInfo extends BaseService
{
    /**
     * 获取ip列表数据
     * @param $list
     * @return void
     * @throws \Exception
     */
    public function getIPList($list)
    {
        foreach ($list as $find) {
            try {
                $lsatDate = IpRequestRecord::where('domain_id', $find['id'])->max('date_added');
                if ($this->curl($find, $lsatDate)) {
                    $record = IpRecord::where('domain_id', $find['id'])->order('id DESC')->field('ip_record_id,date_added')->find();
                    // 获取本次获取的最早时间
                    if ($record) {
                        $start = $record['date_added'];
                    } else {
                        $start = IpRecord::where('domain_id', $find['id'])->order('date_added')->value('date_added');
                    }
                    // 获取本次获取的最晚时间
                    $end = IpRecord::where('domain_id', $find['id'])->order('date_added DESC')->value('date_added');
                    if ($start && $end) {
                        $startTimestamp = strtotime(date('Y-m-d', strtotime($start))); // 开始时间戳
                        $endTimestamp = strtotime(date('Y-m-d', strtotime($end)));   // 结束时间戳
                        for ($timestamp = $startTimestamp; $timestamp <= $endTimestamp; $timestamp += 86400) {
                            $this->ipStatistics($find['id'], date("Y-m-d", $timestamp));
                        }
                    }
                }

            } catch (\ErrorException $e) {
                $this->message(['type' => 'IP统计', 'content' => "【" . $find['domain'] . '】网站程序报错，请尽快处理']);
                Domain::where('id', $find['id'])->update(['status' => 0]);
                echo $find['domain'] . '网站接口访问失败';
                die;
            }
        }
    }

    /**
     * 请求ip抓取接口
     * @param $find
     * @param $lsatDate
     * @return true
     */
    public function curl($find, $lsatDate): bool
    {
        $url = 'https://' . trim($find['domain']) . $find['ip_api'] . '&lastDate=' . $lsatDate;
        $content = self::request_curl($url, 'GET');
        $data = json_decode($content, true);
        if (!isset($data['list'])) {
            $this->message(['type' => 'IP统计', 'content' => "【" . $find['domain'] . '】网站接口访问失败，请尽快处理']);
            Domain::where('id', $find['id'])->update(['status' => 0]);
            echo $find['domain'] . '网站接口访问失败';
            return false;
        }
        // 插入数据库
        // 获取该域名最后一天的所有已收录的ip
        $domain_ips = [];
        if ($lsatDate) {
            $domainIps = IpRecord::where('domain_id', $find['id'])
                ->where('date_added', '>', date('Y-m-d', strtotime($lsatDate)))
                ->column('id,ip_record_id,ip_address,date_added');
            foreach ($domainIps as $ip) {
                $domain_ips[$ip['ip_address'] . '_' . $ip['date_added']] = $ip;
            }
        }

        foreach ($data['list'] as $ip_find) {
            if (!$this->isCrawler($ip_find['http_user_agent'])) {
                $request_record = $ip_find['request_record'];
                // 获取ip
                $ip_info = $domain_ips[$ip_find['ip_address'] . '_' . $ip_find['date_added']] ?? [];
                if (!$ip_info) {
                    $ip_find['domain_id'] = $find['id'];
                    unset($ip_find['request_record']);
                    $info = IpRecord::create($ip_find);
                } else {
                    $info['id'] = $ip_info['id'];
                }
                $request_data = [];
                foreach ($request_record as $ip_val) {
                    if (isset($ip_info['ip_record_id'])) {
                        $ip_val['ip_record_id'] = $ip_info['ip_record_id'];
                    }
                    $ip_val['yy_ip_record_id'] = $info['id'];
                    $ip_val['domain_id'] = $find['id'];
                    $request_data[] = $ip_val;
                    $lsatDate = $ip_val['date_added'];
                }
                IpRequestRecord::limit(1000)->insertAll($request_data);
            }
        }
        if (!empty($data['next'])) {
            $this->curl($find, $lsatDate);
        }
        return true;
    }

    public function test($list)
    {
        $startTimestamp = strtotime(date('Y-m-d', strtotime('2024-02-27'))); // 开始时间戳
        $endTimestamp = strtotime(date('Y-m-d'));   // 结束时间戳
        for ($timestamp = $startTimestamp; $timestamp <= $endTimestamp; $timestamp += 86400) {
            foreach ($list as $find) {
                $this->ipStatistics($find['id'], date("Y-m-d", $timestamp));
            }
        }
    }

    /**
     * ip统计接口
     * @param string $domain_id
     * @param string $date
     * @return true
     * @throws \Exception
     */
    public function ipStatistics(string $domain_id = '', string $date = '')
    {
        if (!$date) {
            $date = date('Y-m-d H:i:s');
        }
        $where = ['domain_id' => $domain_id];
        // 统计
        $data_list = IpRecord::where('date_added', '>', date('Y-m-d', strtotime($date)))
            ->where('date_added', '<=', date('Y-m-d', strtotime($date)) . ' 23:59:59')
            ->where($where)
            ->select()
            ->toArray();

        $data = [];

        foreach ($data_list as $list) {
            $time = strtotime(date('Y-m-d', strtotime($list['date_added'])));
            $domain_id = $list['domain_id'];
            if (empty($list['country_code'])) {
                $ipData = $this->getIp($list['ip_address']);
                $list['country_code'] = $ipData['country_code'];
                $list['continent_code'] = $ipData['continent_code'];
                IpRecord::where('id', $list['id'])->update($list);
            }
            $country_code = $list['country_code'];
            $continent_code = $list['continent_code'];
            $is_zp = $list['is_zp'];
            $is_mobile = $list['is_mobile'];
            $pv_num = IpRequestRecord::where('yy_ip_record_id', $list['id'])->group('http_request')->fetchSql()->count();
halt($pv_num);
            if (!isset($data[$time][$domain_id][$country_code][$continent_code][$is_zp][0]['pv_num'])) {
                $data[$time][$domain_id][$country_code][$continent_code][$is_zp][0]['pv_num'] = 0;
            }
            if (!isset($data[$time][$domain_id][$country_code][$continent_code][$is_zp][1]['pv_num'])) {
                $data[$time][$domain_id][$country_code][$continent_code][$is_zp][1]['pv_num'] = 0;
            }
            if (!isset($data[$time][$domain_id][$country_code][$continent_code][$is_zp][0]['ip_num'])) {
                $data[$time][$domain_id][$country_code][$continent_code][$is_zp][0]['ip_num'] = 0;
            }
            if (!isset($data[$time][$domain_id][$country_code][$continent_code][$is_zp][1]['ip_num'])) {
                $data[$time][$domain_id][$country_code][$continent_code][$is_zp][1]['ip_num'] = 0;
            }

            $data[$time][$domain_id][$country_code][$continent_code][$is_zp][$is_mobile]['pv_num'] += $pv_num;
            $data[$time][$domain_id][$country_code][$continent_code][$is_zp][$is_mobile]['ip_num'] += 1;
        }

        $save = [];
        foreach ($data as $date1 => $datum) {
            foreach ($datum as $domain_id => $item) {
                foreach ($item as $country_code => $value) {
                    foreach ($value as $continent_code => $val) {
                        foreach ($val as $is_zp => $va) {
                            foreach ($va as $is_mobile => $v) {
                                $save[] = [
                                    'date' => $date1,
                                    'domain_id' => $domain_id,
                                    'country_code' => $country_code,
                                    'continent_code' => $continent_code,
                                    'is_zp' => $is_zp,
                                    'is_mobile' => $is_mobile,
                                    'ip_num' => $v['ip_num'],
                                    'pv_num' => $v['pv_num'],
                                ];
                            }
                        }
                    }
                }
            }
        }
        if ($save) {
            IpStatistics::where('date', strtotime(date('Y-m-d', strtotime($date))))->where($where)->delete();
            IpStatistics::insertAll($save);
        }
        return true;
    }

    public function getIp($ip)
    {
        $info = Ip::where('ip', $ip)->find();
        if ($info) {
            return [
                'country_code' => $info['country_code'],
                'continent_code' => $info['continent_code']
            ];
        }
        $ip_species = $this->str_ipv4_ipv6($ip);
        if ($ip_species == 'ipv4') {
            //访问ipv4接口
            $get_url = 'http://apis.juhe.cn/ip/ipNewV3';
            $get_key = '1477d65e6d9feb15524060a2961e16bb';
        } elseif ($ip_species == 'ipv6') {
            $get_url = 'https://apis.juhe.cn/iplocation/ipv6/query';
            $get_key = '016b6f477f27735d84c66810b7635073';
        } elseif ($ip_species == 'no_ip') {
            return 0;
        }

        $ipCheckJson = @file_get_contents($get_url . '?ip=' . $ip . '&key=' . $get_key);
        $ipCheckData = json_decode($ipCheckJson, true);
        $list['country_code'] = $ipCheckData['result']['Country'] ?? $ipCheckData['result']['country'];
        $list['continent_code'] = $ipCheckData['result']['Province'] ?? $ipCheckData['result']['province'];
        Ip::create(['ip' => $ip, 'country_code' => $list['country_code'], 'continent_code' => $list['continent_code']]);
        return $list;
    }

    /**
     * 判断字符串是ipv4还是ipv6
     * @param $str
     * @return string
     */
    public function str_ipv4_ipv6($str): string
    {
        if (filter_var($str, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) {
            return $ip_type = 'ipv4';
        } elseif (filter_var($str, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) {
            return $ip_type = 'ipv6';
        } else {
            return $ip_type = 'no_ip';
        }
    }

    public function isCrawler($http_user_agent)
    {
        $agent = strtolower($http_user_agent);
        if (!empty($agent)) {
            $spiderSite = array(
                "TencentTraveler",
                "Baiduspider+",
                "AhrefsBot",
                "SemrushBot",
                "YandexBot",
                "PetalBot",
                "GoogleOther",
                "Apache-HttpClient",
                "Bytespider",
                "bingbot",
                "DotBot",
                "TelegramBot",
                "DataForSeoBot",
                "serpstatbot",
                "Dataprovider.com",
                "CensysInspect",
                "crawler",
                "SurdotlyBot",
                "DuckDuckGo",
                // "Gecko",
                "BaiduGame",
                "Googlebot",
                "msnbot",
                "Sosospider+",
                "Sogou web spider",
                "ia_archiver",
                "Yahoo! Slurp",
                "YoudaoBot",
                "Yahoo Slurp",
                "MSNBot",
                "Java (Often spam bot)",
                "BaiDuSpider",
                "Voila",
                "Yandex bot",
                "BSpider",
                "twiceler",
                "Sogou Spider",
                "Speedy Spider",
                "Google AdSense",
                "Heritrix",
                "Python-urllib",
                "Alexa (IA Archiver)",
                "Ask",
                "Exabot",
                "Custo",
                "OutfoxBot/YodaoBot",
                "yacy",
                "SurveyBot",
                "legs",
                "lwp-trivial",
                "Nutch",
                "StackRambler",
                "The web archive (IA Archiver)",
                "Perl tool",
                "MJ12bot",
                "Netcraft",
                "MSIECrawler",
                "WGet tools",
                "larbin",
                "Fish search",
            );
            foreach ($spiderSite as $val) {
                $str = strtolower($val);
                if (strpos($agent, $str) !== false) {
                    return true;
                }
            }
        } else {
            return false;
        }
    }
}

